1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <stdio.h>						// For printf()
19#include <Events.h>						// For WaitNextEvent()
20#include <SIOUX.h>						// For SIOUXHandleOneEvent()
21
22#include "mDNSEmbeddedAPI.h"			// Defines the interface to the client layer above
23#include "mDNSMacOS9.h"					// Defines the specific types needed to run mDNS on this platform
24
25typedef struct
26	{
27	OTLIFO serviceinfolist;
28	Boolean headerPrinted;
29	Boolean lostRecords;
30	} SearcherServices;
31
32typedef struct { ServiceInfo i; mDNSBool add; mDNSBool dom; OTLink link; } linkedServiceInfo;
33
34// These don't have to be globals, but their memory does need to remain valid for as
35// long as the search is going on. They are declared as globals here for simplicity.
36#define RR_CACHE_SIZE 1000
37static CacheEntity rrcachestorage[RR_CACHE_SIZE];
38static mDNS mDNSStorage;
39static mDNS_PlatformSupport PlatformSupportStorage;
40static SearcherServices services;
41static DNSQuestion browsequestion, domainquestion;
42
43// PrintServiceInfo prints the service information to standard out
44// A real application might want to do something else with the information
45static void PrintServiceInfo(SearcherServices *services)
46	{
47	OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist));
48
49	while (link)
50		{
51		linkedServiceInfo *ls = OTGetLinkObject(link, linkedServiceInfo, link);
52		ServiceInfo *s = &ls->i;
53
54		if (!services->headerPrinted)
55			{
56			printf("%-55s Type             Domain         IP Address       Port Info\n", "Name");
57			services->headerPrinted = true;
58			}
59
60		if (ls->dom)
61			{
62			char c_dom[MAX_ESCAPED_DOMAIN_NAME];
63			ConvertDomainNameToCString(&s->name, c_dom);
64			if (ls->add) printf("%-55s available for browsing\n", c_dom);
65			else         printf("%-55s no longer available for browsing\n", c_dom);
66			}
67		else
68			{
69			domainlabel name;
70			domainname type, domain;
71			char c_name[MAX_DOMAIN_LABEL+1], c_type[MAX_ESCAPED_DOMAIN_NAME], c_dom[MAX_ESCAPED_DOMAIN_NAME], c_ip[20];
72			DeconstructServiceName(&s->name, &name, &type, &domain);
73			ConvertDomainLabelToCString_unescaped(&name, c_name);
74			ConvertDomainNameToCString(&type, c_type);
75			ConvertDomainNameToCString(&domain, c_dom);
76			sprintf(c_ip, "%d.%d.%d.%d", s->ip.ip.v4.b[0], s->ip.ip.v4.b[1], s->ip.ip.v4.b[2], s->ip.ip.v4.b[3]);
77
78			printf("%-55s %-16s %-14s ", c_name, c_type, c_dom);
79			if (ls->add) printf("%-15s %5d %#s\n", c_ip, mDNSVal16(s->port), s->TXTinfo);
80			else         printf("Removed\n");
81			}
82
83		link = link->fNext;
84		OTFreeMem(ls);
85		}
86	}
87
88// When the name, address, port, and txtinfo for a service is found, FoundInstanceInfo()
89// enqueues a record for PrintServiceInfo() to print.
90// Note, a browsing application would *not* normally need to get all this information --
91// all it needs is the name, to display to the user.
92// Finding out the address, port, and txtinfo should be deferred to the time that the user
93// actually needs to contact the service to use it.
94static void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query)
95	{
96	SearcherServices *services = (SearcherServices *)query->ServiceInfoQueryContext;
97	linkedServiceInfo *info = (linkedServiceInfo *)(query->info);
98	if (query->info->ip.type == mDNSAddrType_IPv4)
99		{
100		mDNS_StopResolveService(m, query);		// For this test code, one answer is sufficient
101		OTLIFOEnqueue(&services->serviceinfolist, &info->link);
102		OTFreeMem(query);
103		}
104	}
105
106// When a new named instance of a service is found, FoundInstance() is called.
107// In this sample code we turn around and immediately issue a query to resolve that service name to
108// find its address, port, and txtinfo, but a normal browing application would just display the name.
109static void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
110	{
111	#pragma unused (question)
112	SearcherServices *services = (SearcherServices *)question->QuestionContext;
113	linkedServiceInfo *info;
114
115	debugf("FoundInstance %##s PTR %##s", answer->name->c, answer->rdata->u.name.c);
116
117	if (answer->rrtype != kDNSType_PTR) return;
118	if (!services) { debugf("FoundInstance: services is NULL"); return; }
119
120	info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo));
121	if (!info) { services->lostRecords = true; return; }
122
123	info->i.name          = answer->rdata->u.name;
124	info->i.InterfaceID   = answer->InterfaceID;
125	info->i.ip.type		  = mDNSAddrType_IPv4;
126	info->i.ip.ip.v4      = zerov4Addr;
127	info->i.port          = zeroIPPort;
128	info->add             = AddRecord;
129	info->dom             = mDNSfalse;
130
131	if (!AddRecord)	// If TTL == 0 we're deleting a service,
132		OTLIFOEnqueue(&services->serviceinfolist, &info->link);
133	else								// else we're adding a new service
134		{
135		ServiceInfoQuery *q = (ServiceInfoQuery *)OTAllocMem(sizeof(ServiceInfoQuery));
136		if (!q) { OTFreeMem(info); services->lostRecords = true; return; }
137		mDNS_StartResolveService(m, q, &info->i, FoundInstanceInfo, services);
138		}
139	}
140
141static void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
142	{
143	#pragma unused (m)
144	#pragma unused (question)
145	SearcherServices *services = (SearcherServices *)question->QuestionContext;
146	linkedServiceInfo *info;
147
148	debugf("FoundDomain %##s PTR %##s", answer->name->c, answer->rdata->u.name.c);
149
150	if (answer->rrtype != kDNSType_PTR) return;
151	if (!services) { debugf("FoundDomain: services is NULL"); return; }
152
153	info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo));
154	if (!info) { services->lostRecords = true; return; }
155
156	info->i.name          = answer->rdata->u.name;
157	info->i.InterfaceID   = answer->InterfaceID;
158	info->i.ip.type		  = mDNSAddrType_IPv4;
159	info->i.ip.ip.v4      = zerov4Addr;
160	info->i.port          = zeroIPPort;
161	info->add             = AddRecord;
162	info->dom             = mDNStrue;
163
164	OTLIFOEnqueue(&services->serviceinfolist, &info->link);
165	}
166
167// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS
168static Boolean YieldSomeTime(UInt32 milliseconds)
169	{
170	extern Boolean SIOUXQuitting;
171	EventRecord e;
172	WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL);
173	SIOUXHandleOneEvent(&e);
174	return(SIOUXQuitting);
175	}
176
177int main()
178	{
179	mStatus err;
180	Boolean DoneSetup = false;
181	void *tempmem;
182
183	SIOUXSettings.asktosaveonclose = false;
184	SIOUXSettings.userwindowtitle  = "\pMulticast DNS Searcher";
185	SIOUXSettings.rows             = 40;
186	SIOUXSettings.columns          = 132;
187
188	printf("Multicast DNS Searcher\n\n");
189	printf("This software reports errors using MacsBug breaks,\n");
190	printf("so if you don't have MacsBug installed your Mac may crash.\n\n");
191	printf("******************************************************************************\n");
192
193	err = InitOpenTransport();
194	if (err) { debugf("InitOpenTransport failed %d", err); return(err); }
195
196	err = mDNS_Init(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE,
197		mDNS_Init_DontAdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
198	if (err) return(err);
199
200	// Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time
201	tempmem = OTAllocMem(0x10000);
202	if (tempmem) OTFreeMem(tempmem);
203	else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n");
204
205	services.serviceinfolist.fHead = NULL;
206	services.headerPrinted         = false;
207	services.lostRecords           = false;
208
209	while (!YieldSomeTime(35))
210		{
211#if MDNS_ONLYSYSTEMTASK
212		// For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically.
213		// For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle()
214		extern void mDNSPlatformIdle(mDNS *const m);
215		mDNSPlatformIdle(&mDNSStorage);	// Only needed for debugging version
216#endif
217		if (mDNSStorage.mDNSPlatformStatus == mStatus_NoError && !DoneSetup)
218			{
219			domainname srvtype, srvdom;
220			DoneSetup = true;
221			printf("\nSending mDNS service lookup queries and waiting for responses...\n\n");
222			MakeDomainNameFromDNSNameString(&srvtype, "_http._tcp.");
223			MakeDomainNameFromDNSNameString(&srvdom, "local.");
224			err = mDNS_StartBrowse(&mDNSStorage, &browsequestion, &srvtype, &srvdom, mDNSInterface_Any, mDNSfalse, FoundInstance, &services);
225			if (err) break;
226			err = mDNS_GetDomains(&mDNSStorage, &domainquestion, mDNS_DomainTypeBrowse, NULL, mDNSInterface_Any, FoundDomain, &services);
227			if (err) break;
228			}
229
230		if (services.serviceinfolist.fHead)
231			PrintServiceInfo(&services);
232
233		if (services.lostRecords)
234			{
235			services.lostRecords = false;
236			printf("**** Warning: Out of memory: Records have been missed.\n");
237			}
238		}
239
240	mDNS_StopBrowse(&mDNSStorage, &browsequestion);
241	mDNS_Close(&mDNSStorage);
242	return(0);
243	}
244